<?php

defined('BASEPATH') or die('forbidden');

/**
 * 模板类
 * @author  Karson
 * @name    template.php
 * @since   2011-11-18 18:13:23
 */
class Template {

    var $dir;               //模板目录
    var $cache;             //缓存目录
    var $filter;            //过滤空格换行等字符
    var $suffix;            //模板文件后缀
    private $data = array(); //模板中需要显示的数据

    function __construct() {
        $this->dir = TEMPLATE_DIR;
        $this->cache = TEMPLATE_CACHE;
        $this->filter = TEMPLATE_FILTER;
        $this->suffix = TEMPLATE_SUFFIX;
    }

    /**
     * 设置模板中显示的变量
     * @param string $key 变量名称
     * @param mixed $value 变量值
     */
    function assign($key, $value = null) {
        if (isset($value)) {
            $this->data[$key] = $value;
        } else {
            foreach ($key as $k => $v) {
                if (is_int($k)) {
                    $this->data[] = $v;
                } else {
                    $this->data[$key] = $v;
                }
            }
        }
    }

    /**
     * 返回一个模板解析后的结果,不执行输出
     * @param string $name 模板文件名称
     * @param mixed $data 向模板传输的数据，默认为null
     * @return string 解析后的数据
     */
    function fetch($name, $data = null) {
        ob_start();
        $this->display($name, $data);
        $content = ob_get_contents();
        ob_end_clean();
        return $content;
    }

    /**
     * 显示一个模板解析后的结果，执行输出
     * @param string $name 模板文件名称, 后缀可省略
     * @param mixed $data 向模板传输的数据，默认为null
     */
    function display($name, $data = null) {
        /* 把查询信息和调试及信息状态信息写入模板 */
        $framework = &get_instance('controller');
        $data['querycounts'] = $framework->db->querycounts;
        $data['debuginfo'] = $framework->db->debuginfo;
        $data['user'] = $framework->auth->fields;
        $data['version'] = $framework->config['version'];
        $data['config'] = $framework->config;
        $data['referer_url'] = $framework->input->server('HTTP_REFERER');
        $data['current_url'] = get_url();
        $data['current_time'] = time();
        $path = $this->parse($name);
        return $this->_view($path, $data);
    }

    /**
     * 显示一个模板解析后的结果，执行输出，同display
     * @param string $name 模板文件名称
     * @param mixed $data 向模板传输的数据，默认为null
     */
    function view($name, $data = null) {
        $this->display($name, $data);
    }

    /**
     * 显示并解析模板
     * @param string $path 模板文件路径
     * @param mixed $data 向模板输出的数据，默认为null
     */
    function _view($path, $data = null) {
        extract($this->data);
        if (!empty($data))
            extract($data);
        include ($path);
    }

    /**
     * 抛出一个错误信息
     * @param string $msg 错误信息内容
     */
    function error($msg) {
        throw new Exception($msg);
    }

    /**
     * 解析模板
     * @param string $view 模板文件名称
     * @return string 返回解析后的内容
     */
    function parse($name) {
        $name = preg_replace('/\\' . $this->suffix . '/i', '', $name);
        $view = $this->dir . '/' . $name . $this->suffix;
        $cache = $this->cache . '/' . $name . '.php';

        if (!file_exists($view))
            $this->error("模板文件[{$name}{$this->suffix}]不存在！");

        //如果缓存文件存在且未过期
        if (file_exists($cache) && @filemtime($view) < @filemtime($cache)) {
            return $cache;
        }

        //获取模板文件内容
        $view = file_get_contents($view);

        /* 将<!--{ }-->替换为{ } */
        $view = preg_replace("/\<\!\-\-\{(.+?)\}\-\-\>/s", "{\\1}", $view);
        /* 清除注释 */
        $view = preg_replace("/\{\*(.*?)\*\}/", '', $view);
        /* 解析{ $var }基本变量输出标签 */
        $view = preg_replace('/\{\$(.+?)\}/', '<?php echo $\\1; ?>', $view);
        /* 解析常量 */
        $view = preg_replace('#\{\s*([A-Z]+)\s*\}#', '<?php echo \\1; ?>', $view);
        /* 解析{ include file=}标签 */
        $view = preg_replace('/\{\s*include\s+file=[\'"]?(.+?)[\'"]?\s*\}/', '{template \\1}', $view);
        /* 解析{ include }标签 */
        $view = preg_replace('/\{\s*include\s+(.+?)\s*\}/', '<?php include APPPATH.\'views/\\1\'; ?>', $view);
        /* 解析{ html  }标签 */
        $view = preg_replace('/\{\s*html\s+(.+?)\s*\}/', '<?php echo file_get_contents(\\1); ?>', $view);
        /* 解析{ php  }标签 */
        $view = preg_replace('/\{\s*php\s+(.+?)\s*\}/', '<?php \\1 ?>', $view);
        /* 解析{ if  }标签 */
        $view = preg_replace('/\{\s*if\s+(.+?)\s*\}/', '<?php if (\\1) { ?>', $view);
        /* 解析{ elseif  }标签 */
        $view = preg_replace('/\{\s*else\s*if\s+(.+?)\s*\}/', '<?php } else if(\\1) { ?>', $view);
        /* 解析{ else }标签 */
        $view = preg_replace('/\{\s*else\s*\}/', '<?php } else { ?>', $view);
        /* 解析{ while }标签 */
        $view = preg_replace('/\{\s*while\s+(.+?)\s*\}/', '<?php while (\\1) { ?>', $view);
        /* 解析{ foreach }标签 */
        $view = preg_replace('/\{\s*foreach\s+(.+?)\s*\}/', '<?php foreach (\\1) { ?>', $view);
        /* 解析{ for }标签 */
        $view = preg_replace('/\{\s*for\s+(.+?)\s*\}/', '<?php for (\\1) { ?>', $view);
        $view = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\s*\}(.+?)\{\/loop\}/ies", "__replace('<?php if(is_array(\\1)){foreach(\\1 AS \\2=>\\3) { ?>\\4<?php }}?>')", $view);
        $view = preg_replace("/\{loop\s+(\S+)\s+(\S+)\s*\}(.+?)\{\/loop\}/ies", "__replace('<?php if(is_array(\\1)){foreach(\\1 AS \\2) { ?>\\3<?php }}?>')", $view);

        /* 解析{ /while },{ /if },{ /for },{ /foreach }标签 */
        $view = preg_replace('#\{\s*/[a-z]+\s*\}#', '<?php } ?>', $view);
        /* 解析子模板 */
        $view = preg_replace('/\{\s*template\s+(.+?)\s*\}/', '<?php include $this->parse("\\1"); ?>', $view);
        /* 如果开启了缓存过滤，则过滤掉其中多余空格、Tab、回车 */
        if ($this->filter)
            $view = preg_replace("/ \?\>[\n\r]*\<\? /s", " ", $view);
        /* 合并相邻标签的解析结果 */
        $view = preg_replace('/\?>\s*<\?php/', '', $view);
        $view = preg_replace('/\?>\s*<\?/', '', $view);

        mk_dir(dirname($cache));
        $view = '<?' . 'php defined("BASEPATH") or die("非法请求"); ' . '?>' . $view;
        @file_put_contents($cache, $view);
        return $cache;
    }

}

function __replace($string) {
    return str_replace('\"', '"', $string);
}